// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "GameFramework/GameUserSettings.h"
#include "InputCoreTypes.h"

#include "LyraSettingsLocal.generated.h"

// 前向声明枚举
enum class ECommonInputType : uint8;
enum class ELyraDisplayablePerformanceStat : uint8;
enum class ELyraStatDisplayMode : uint8;

// 前向声明类
class ULyraLocalPlayer;
class UObject;
class USoundControlBus;
class USoundControlBusMix;
struct FFrame;

/**
 * Lyra可伸缩性快照结构体
 */
USTRUCT()
struct FLyraScalabilitySnapshot
{
	GENERATED_BODY()

	FLyraScalabilitySnapshot();

	Scalability::FQualityLevels Qualities; // 质量级别
	bool bActive = false; // 是否激活
	bool bHasOverrides = false; // 是否有覆盖
};

/**
 * Lyra本地设置类
 */
UCLASS()
class ULyraSettingsLocal : public UGameUserSettings
{
	GENERATED_BODY()

public:

	ULyraSettingsLocal();

	/**
	 * 获取Lyra本地设置实例
	 * @return Lyra本地设置实例
	 */
	static ULyraSettingsLocal* Get();

	//~UObject接口
	/**
	 * 开始销毁对象
	 */
	virtual void BeginDestroy() override;
	//~UObject接口结束

	//~UGameUserSettings接口
	/**
	 * 设置为默认值
	 */
	virtual void SetToDefaults() override;
	
	/**
	 * 加载设置
	 * @param bForceReload 是否强制重新加载
	 */
	virtual void LoadSettings(bool bForceReload) override;
	
	/**
	 * 确认视频模式
	 */
	virtual void ConfirmVideoMode() override;
	
	/**
	 * 获取有效帧率限制
	 * @return 有效帧率限制
	 */
	virtual float GetEffectiveFrameRateLimit() override;
	
	/**
	 * 重置为当前设置
	 */
	virtual void ResetToCurrentSettings() override;
	
	/**
	 * 应用非分辨率设置
	 */
	virtual void ApplyNonResolutionSettings() override;
	
	/**
	 * 获取整体可伸缩性级别
	 * @return 整体可伸缩性级别
	 */
	virtual int32 GetOverallScalabilityLevel() const override;
	
	/**
	 * 设置整体可伸缩性级别
	 * @param Value 可伸缩性级别值
	 */
	virtual void SetOverallScalabilityLevel(int32 Value) override;
	//~UGameUserSettings接口结束

	/**
	 * 经验加载完成时调用
	 */
	void OnExperienceLoaded();
	
	/**
	 * 热修复设备配置文件应用时调用
	 */
	void OnHotfixDeviceProfileApplied();

	//////////////////////////////////////////////////////////////////
	// 前端状态

public:
	/**
	 * 设置是否应使用前端性能设置
	 * @param bInFrontEnd 是否在前端
	 */
	void SetShouldUseFrontendPerformanceSettings(bool bInFrontEnd);
	
protected:
	/**
	 * 检查是否应使用前端性能设置
	 * @return 如果应使用前端性能设置返回true，否则返回false
	 */
	bool ShouldUseFrontendPerformanceSettings() const;
	
private:
	bool bInFrontEndForPerformancePurposes = false; // 是否出于性能目的在前端

	//////////////////////////////////////////////////////////////////
	// 性能统计

public:
	/** 返回指定性能统计的显示模式 */
	UFUNCTION()
	ELyraStatDisplayMode GetPerfStatDisplayState(ELyraDisplayablePerformanceStat Stat) const;
	
	/** 设置指定性能统计的显示模式 */
	UFUNCTION()
	void SetPerfStatDisplayState(ELyraDisplayablePerformanceStat Stat, ELyraStatDisplayMode DisplayMode);

	/** 当性能统计显示状态更改或应用设置时触发 */
	DECLARE_EVENT(ULyraSettingsLocal, FPerfStatSettingsChanged);
	FPerfStatSettingsChanged& OnPerfStatDisplayStateChanged() { return PerfStatSettingsChangedEvent; }

	// 延迟闪光指示器
	/** 检查平台是否支持延迟标记 */
	UFUNCTION()
	static bool DoesPlatformSupportLatencyMarkers();
	
	DECLARE_EVENT(ULyraSettingsLocal, FLatencyFlashInidicatorSettingChanged);
	/** 设置启用延迟闪光指示器 */
	UFUNCTION()
	void SetEnableLatencyFlashIndicators(const bool bNewVal);
	
	/** 获取延迟闪光指示器启用状态 */
	UFUNCTION()
	bool GetEnableLatencyFlashIndicators() const { return bEnableLatencyFlashIndicators; }
	
	FLatencyFlashInidicatorSettingChanged& OnLatencyFlashInidicatorSettingsChangedEvent() { return LatencyFlashInidicatorSettingsChangedEvent; }

	// 延迟跟踪统计
	/** 检查平台是否支持延迟跟踪统计 */
	UFUNCTION()
	static bool DoesPlatformSupportLatencyTrackingStats();
	
	DECLARE_EVENT(ULyraSettingsLocal, FLatencyStatEnabledSettingChanged);
	FLatencyStatEnabledSettingChanged& OnLatencyStatIndicatorSettingsChangedEvent() { return LatencyStatIndicatorSettingsChangedEvent; }
	
	/** 设置启用延迟跟踪统计 */
	UFUNCTION()
	void SetEnableLatencyTrackingStats(const bool bNewVal);
	
	/** 获取延迟跟踪统计启用状态 */
	UFUNCTION()
	bool GetEnableLatencyTrackingStats() const { return bEnableLatencyTrackingStats; }

private:
	/**
	 * 应用延迟跟踪统计设置
	 */
	void ApplyLatencyTrackingStatSetting();
	
	// 要在HUD中显示的统计列表
	UPROPERTY(Config)
	TMap<ELyraDisplayablePerformanceStat, ELyraStatDisplayMode> DisplayStatList; // 显示统计列表

	// 显示统计小部件容器绑定的事件
	FPerfStatSettingsChanged PerfStatSettingsChangedEvent; // 性能统计设置更改事件

	// 如果为true，启用延迟闪光标记，可用于测量输入延迟
	UPROPERTY(Config)
	bool bEnableLatencyFlashIndicators = false; // 启用延迟闪光指示器，默认为false

	// 延迟闪光指示器设置更改时玩家输入绑定的事件
	FLatencyFlashInidicatorSettingChanged LatencyFlashInidicatorSettingsChangedEvent; // 延迟闪光指示器设置更改事件

	// 延迟统计启用或禁用更改时的事件
	FLatencyStatEnabledSettingChanged LatencyStatIndicatorSettingsChangedEvent; // 延迟统计指示器设置更改事件

	// 如果为true，则游戏将通过ILatencyMarkerModule模块跟踪延迟统计
	// 这使您可以查看更多面向延迟的性能统计
	// 如果平台支持，默认值为true，否则为false
	UPROPERTY(Config)
	bool bEnableLatencyTrackingStats; // 启用延迟跟踪统计

	//////////////////////////////////////////////////////////////////
	// 亮度/伽马

public:
	/** 获取显示伽马值 */
	UFUNCTION()
	float GetDisplayGamma() const;
	
	/** 设置显示伽马值 */
	UFUNCTION()
	void SetDisplayGamma(float InGamma);

private:
	/**
	 * 应用显示伽马设置
	 */
	void ApplyDisplayGamma();
	
	UPROPERTY(Config)
	float DisplayGamma = 2.2; // 显示伽马值，默认为2.2

	//////////////////////////////////////////////////////////////////
	// 显示

public:
	/** 获取电池供电时的帧率限制 */
	UFUNCTION()
	float GetFrameRateLimit_OnBattery() const;
	
	/** 设置电池供电时的帧率限制 */
	UFUNCTION()
	void SetFrameRateLimit_OnBattery(float NewLimitFPS);

	/** 获取菜单中的帧率限制 */
	UFUNCTION()
	float GetFrameRateLimit_InMenu() const;
	
	/** 设置菜单中的帧率限制 */
	UFUNCTION()
	void SetFrameRateLimit_InMenu(float NewLimitFPS);

	/** 获取后台运行时的帧率限制 */
	UFUNCTION()
	float GetFrameRateLimit_WhenBackgrounded() const;
	
	/** 设置后台运行时的帧率限制 */
	UFUNCTION()
	void SetFrameRateLimit_WhenBackgrounded(float NewLimitFPS);

	/** 获取始终应用的帧率限制 */
	UFUNCTION()
	float GetFrameRateLimit_Always() const;
	
	/** 设置始终应用的帧率限制 */
	UFUNCTION()
	void SetFrameRateLimit_Always(float NewLimitFPS);

protected:
	/**
	 * 更新有效帧率限制
	 */
	void UpdateEffectiveFrameRateLimit();

private:
	UPROPERTY(Config)
	float FrameRateLimit_OnBattery; // 电池供电时的帧率限制
	UPROPERTY(Config)
	float FrameRateLimit_InMenu; // 菜单中的帧率限制
	UPROPERTY(Config)
	float FrameRateLimit_WhenBackgrounded; // 后台运行时的帧率限制

	//////////////////////////////////////////////////////////////////
	// 显示 - 移动设备质量设置

public:
	/** 获取默认移动设备帧率 */
	UFUNCTION()
	static int32 GetDefaultMobileFrameRate();
	
	/** 获取最大移动设备帧率 */
	UFUNCTION()
	static int32 GetMaxMobileFrameRate();

	/** 检查是否支持移动设备帧率步调 */
	UFUNCTION()
	static bool IsSupportedMobileFramePace(int32 TestFPS);

	/** 返回第一个受当前设备配置文件限制整体质量的帧率 */
	UFUNCTION()
	int32 GetFirstFrameRateWithQualityLimit() const;

	/** 返回存在整体帧率限制的最低质量（如果没有限制则返回-1） */
	UFUNCTION()
	int32 GetLowestQualityWithFrameRateLimit() const;

	/** 重置为移动设备默认值 */
	UFUNCTION()
	void ResetToMobileDeviceDefaults();

	/** 获取最大支持的总体质量级别 */
	UFUNCTION()
	int32 GetMaxSupportedOverallQualityLevel() const;

private:
	/**
	 * 设置移动设备FPS模式
	 * @param NewLimitFPS 新的帧率限制
	 */
	void SetMobileFPSMode(int32 NewLimitFPS);

	/**
	 * 钳制移动设备分辨率质量
	 * @param TargetFPS 目标帧率
	 */
	void ClampMobileResolutionQuality(int32 TargetFPS);
	
	/**
	 * 重新映射移动设备分辨率质量
	 * @param FromFPS 源帧率
	 * @param ToFPS 目标帧率
	 */
	void RemapMobileResolutionQuality(int32 FromFPS, int32 ToFPS);

	/**
	 * 钳制移动设备FPS质量级别
	 * @param bWriteBack 是否写回
	 */
	void ClampMobileFPSQualityLevels(bool bWriteBack);
	
	/**
	 * 钳制移动设备质量
	 */
	void ClampMobileQuality();
	
	/**
	 * 获取任何可伸缩性通道的最高级别
	 * @return 最高质量级别
	 */
	int32 GetHighestLevelOfAnyScalabilityChannel() const;

	/** 根据活动模式的覆盖修改输入级别 */
	void OverrideQualityLevelsToScalabilityMode(const FLyraScalabilitySnapshot& InMode, Scalability::FQualityLevels& InOutLevels);

	/** 根据活动设备配置文件的默认允许级别钳制输入级别 */
	void ClampQualityLevelsToDeviceProfile(const Scalability::FQualityLevels& ClampLevels, Scalability::FQualityLevels& InOutLevels);

public:
	/** 获取期望的移动设备帧率限制 */
	UFUNCTION()
	int32 GetDesiredMobileFrameRateLimit() const { return DesiredMobileFrameRateLimit; }

	/** 设置期望的移动设备帧率限制 */
	UFUNCTION()
	void SetDesiredMobileFrameRateLimit(int32 NewLimitFPS);

private:
	UPROPERTY(Config)
	int32 MobileFrameRateLimit = 30; // 移动设备帧率限制，默认为30

	FLyraScalabilitySnapshot DeviceDefaultScalabilitySettings; // 设备默认可伸缩性设置

	bool bSettingOverallQualityGuard = false; // 设置整体质量保护标志

	int32 DesiredMobileFrameRateLimit = 0; // 期望的移动设备帧率限制

private:

	//////////////////////////////////////////////////////////////////
	// 显示 - 控制台质量预设

public:
	/** 获取期望的设备配置文件质量后缀 */
	UFUNCTION()
	FString GetDesiredDeviceProfileQualitySuffix() const;
	
	/** 设置期望的设备配置文件质量后缀 */
	UFUNCTION()
	void SetDesiredDeviceProfileQualitySuffix(const FString& InDesiredSuffix);

protected:
	/** 更新当前游戏模式的设备配置文件和FPS */
	void UpdateGameModeDeviceProfileAndFps();

	/** 更新控制台帧步调 */
	void UpdateConsoleFramePacing();
	
	/** 更新桌面帧步调 */
	void UpdateDesktopFramePacing();
	
	/** 更新移动设备帧步调 */
	void UpdateMobileFramePacing();

	/** 更新动态分辨率帧时间 */
	void UpdateDynamicResFrameTime(float TargetFPS);

private:
	UPROPERTY(Transient)
	FString DesiredUserChosenDeviceProfileSuffix; // 期望的用户选择设备配置文件后缀

	UPROPERTY(Transient)
	FString CurrentAppliedDeviceProfileOverrideSuffix; // 当前应用的设备配置文件覆盖后缀

	UPROPERTY(config)
	FString UserChosenDeviceProfileSuffix; // 用户选择的设备配置文件后缀

	//////////////////////////////////////////////////////////////////
	// 音频 - 音量

public:
	// 音频设备更改事件
	DECLARE_EVENT_OneParam(ULyraSettingsLocal, FAudioDeviceChanged, const FString& /*DeviceId*/);
	FAudioDeviceChanged OnAudioOutputDeviceChanged;

public:
	/** 返回是否使用耳机模式（HRTF） */
	UFUNCTION()
	bool IsHeadphoneModeEnabled() const;

	/** 启用或禁用耳机模式（HRTF）- 注意：如果设置了au.DisableBinauralSpatialization，此设置将被覆盖 */
	UFUNCTION()
	void SetHeadphoneModeEnabled(bool bEnabled);

	/** 返回是否可以启用/禁用耳机模式（即，平台未强制打开或关闭） */
	UFUNCTION()
	bool CanModifyHeadphoneModeEnabled() const;

public:
	/** 我们是否*想要*使用耳机模式（HRTF）；可能实际应用，也可能不应用 */
	UPROPERTY(Transient)
	bool bDesiredHeadphoneMode; // 期望的耳机模式

private:
	/** 是否使用耳机模式（HRTF） */
	UPROPERTY(config)
	bool bUseHeadphoneMode; // 使用耳机模式

public:
	/** 返回是否使用高动态范围音频模式（HDR音频） */
	UFUNCTION()
	bool IsHDRAudioModeEnabled() const;

	/** 启用或禁用高动态范围音频模式（HDR音频） */
	UFUNCTION()
	void SetHDRAudioModeEnabled(bool bEnabled);

	/** 是否使用高动态范围音频模式（HDR音频） */
	UPROPERTY(config)
	bool bUseHDRAudioMode; // 使用HDR音频模式

public:
	/** 返回此平台是否可以运行自动基准测试 */
	UFUNCTION(BlueprintCallable, Category = Settings)
	bool CanRunAutoBenchmark() const;

	/** 返回此用户是否应在启动时运行自动基准测试，因为它从未运行过 */
	UFUNCTION(BlueprintCallable, Category = Settings)
	bool ShouldRunAutoBenchmarkAtStartup() const;

	/** 运行自动基准测试，可选择立即保存 */
	UFUNCTION(BlueprintCallable, Category = Settings)
	void RunAutoBenchmark(bool bSaveImmediately);

	/** 仅应用质量可伸缩性设置 */
	void ApplyScalabilitySettings();

	/** 获取总体音量 */
	UFUNCTION()
	float GetOverallVolume() const;
	
	/** 设置总体音量 */
	UFUNCTION()
	void SetOverallVolume(float InVolume);

	/** 获取音乐音量 */
	UFUNCTION()
	float GetMusicVolume() const;
	
	/** 设置音乐音量 */
	UFUNCTION()
	void SetMusicVolume(float InVolume);

	/** 获取音效音量 */
	UFUNCTION()
	float GetSoundFXVolume() const;
	
	/** 设置音效音量 */
	UFUNCTION()
	void SetSoundFXVolume(float InVolume);

	/** 获取对话音量 */
	UFUNCTION()
	float GetDialogueVolume() const;
	
	/** 设置对话音量 */
	UFUNCTION()
	void SetDialogueVolume(float InVolume);

	/** 获取语音聊天音量 */
	UFUNCTION()
	float GetVoiceChatVolume() const;
	
	/** 设置语音聊天音量 */
	UFUNCTION()
	void SetVoiceChatVolume(float InVolume);

	//////////////////////////////////////////////////////////////////
	// 音频 - 声音

public:
	/** 返回用户的音频设备ID */
	UFUNCTION()
	FString GetAudioOutputDeviceId() const { return AudioOutputDeviceId; }

	/** 通过ID设置用户的音频设备 */
	UFUNCTION()
	void SetAudioOutputDeviceId(const FString& InAudioOutputDeviceId);

private:
	UPROPERTY(Config)
	FString AudioOutputDeviceId; // 音频输出设备ID
	
	/**
	 * 为声音类设置音量
	 * @param ChannelName 通道名称
	 * @param InVolume 音量值
	 */
	void SetVolumeForSoundClass(FName ChannelName, float InVolume);
	

	//////////////////////////////////////////////////////////////////
	// 安全区域

public:
	/** 检查安全区域是否已设置 */
	UFUNCTION()
	bool IsSafeZoneSet() const { return SafeZoneScale != -1; }
	
	/** 获取安全区域比例 */
	UFUNCTION()
	float GetSafeZone() const { return SafeZoneScale >= 0 ? SafeZoneScale : 0; }
	
	/** 设置安全区域比例 */
	UFUNCTION()
	void SetSafeZone(float Value) { SafeZoneScale = Value; ApplySafeZoneScale(); }

	/** 应用安全区域比例 */
	UFUNCTION()
	void ApplySafeZoneScale();
	
private:
	/**
	 * 为控制总线设置音量
	 * @param InSoundControlBus 声音控制总线
	 * @param InVolume 音量值
	 */
	void SetVolumeForControlBus(USoundControlBus* InSoundControlBus, float InVolume);

	//////////////////////////////////////////////////////////////////
	// 键位绑定

public:
	/** 设置要使用的控制器表示，单个平台可能支持多种控制器 */
	UFUNCTION()
	void SetControllerPlatform(const FName InControllerPlatform);
	
	/** 获取控制器平台 */
	UFUNCTION()
	FName GetControllerPlatform() const;

private:
	/**
	 * 加载用户控制总线混合
	 */
	void LoadUserControlBusMix();

	UPROPERTY(Config)
	float OverallVolume = 1.0f; // 总体音量，默认为1.0
	UPROPERTY(Config)
	float MusicVolume = 1.0f; // 音乐音量，默认为1.0
	UPROPERTY(Config)
	float SoundFXVolume = 1.0f; // 音效音量，默认为1.0
	UPROPERTY(Config)
	float DialogueVolume = 1.0f; // 对话音量，默认为1.0
	UPROPERTY(Config)
	float VoiceChatVolume = 1.0f; // 语音聊天音量，默认为1.0

	UPROPERTY(Transient)
	TMap<FName/*SoundClassName*/, TObjectPtr<USoundControlBus>> ControlBusMap; // 控制总线映射

	UPROPERTY(Transient)
	TObjectPtr<USoundControlBusMix> ControlBusMix = nullptr; // 控制总线混合

	UPROPERTY(Transient)
	bool bSoundControlBusMixLoaded; // 声音控制总线混合是否已加载

	UPROPERTY(Config)
	float SafeZoneScale = -1; // 安全区域比例，默认为-1

	/**
	 * 玩家正在使用的控制器名称。这映射到当前平台上可用的UCommonInputBaseControllerData的名称
	 * 游戏手柄数据按平台注册，您可以在<Platform>Game.ini文件中找到它们，列在+ControllerData=...下
	 */
	UPROPERTY(Config)
	FName ControllerPlatform; // 控制器平台

	UPROPERTY(Config)
	FName ControllerPreset = TEXT("Default"); // 控制器预设，默认为"Default"

	/** 用户选择的当前输入配置的名称 */
	UPROPERTY(Config)
	FName InputConfigName = TEXT("Default"); // 输入配置名称，默认为"Default"

	// 回放
public:
	/** 获取是否应自动录制回放 */
	UFUNCTION()
	bool ShouldAutoRecordReplays() const { return bShouldAutoRecordReplays; }
	
	/** 设置是否应自动录制回放 */
	UFUNCTION()
	void SetShouldAutoRecordReplays(bool bEnabled) { bShouldAutoRecordReplays = bEnabled;}

	/** 获取要保留的回放数量 */
	UFUNCTION()
	int32 GetNumberOfReplaysToKeep() const { return NumberOfReplaysToKeep; }
	
	/** 设置要保留的回放数量 */
	UFUNCTION()
	void SetNumberOfReplaysToKeep(int32 InNumberOfReplays) { NumberOfReplaysToKeep = InNumberOfReplays; }

private:
	UPROPERTY(Config)
	bool bShouldAutoRecordReplays = false; // 是否应自动录制回放，默认为false

	UPROPERTY(Config)
	int32 NumberOfReplaysToKeep = 5; // 要保留的回放数量，默认为5

private:
	/**
	 * 应用程序激活状态更改时调用
	 * @param bIsActive 是否激活
	 */
	void OnAppActivationStateChanged(bool bIsActive);
	
	/**
	 * 由于可能的设备配置文件更改而重新应用事项
	 */
	void ReapplyThingsDueToPossibleDeviceProfileChange();

private:
	FDelegateHandle OnApplicationActivationStateChangedHandle; // 应用程序激活状态更改句柄
};